package top.lingkang.finalgateway.utils;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import org.dom4j.*;
import org.dom4j.tree.DefaultElement;
import top.lingkang.finalgateway.admin.constants.PassType;
import top.lingkang.finalgateway.config.*;

import java.util.ArrayList;
import java.util.List;

/**
 * @author lingkang
 * @create by 2024/5/23 10:34
 */
public class ConfigUtils {
    /**
     * 加载xml配置
     *
     * @param xml 配置string
     */
    public static GatewayConfig loadXML(String xml) {
        Document document = null;
        try {
            document = DocumentHelper.parseText(xml);
        } catch (DocumentException e) {
            throw new RuntimeException("加载xml异常：" + e.getMessage(), e);
        }
        Element config = document.getRootElement();
        GatewayConfig gateway = new GatewayConfig();
        gateway.setRootXmlConfig(config);
        gateway.setXml(xml);

        // 加载router
        List<Element> router_ = config.elements("router");
        for (Node r : router_) {
            DefaultElement router = (DefaultElement) r;
            List<Element> router_property = router.elements("property");
            RouterConfig routerConfig = new RouterConfig();

            boolean existsPort = false;
            // router 配置
            for (Element property : router_property) {
                String name = property.attributeValue("name");
                if (StrUtil.isBlank(name))
                    continue;
                name = name.toLowerCase();
                String value = getPropertyValue(property);
                if ("enable".equals(name)) {
                    routerConfig.setEnable(Boolean.parseBoolean(value));
                } else if ("port".equals(name)) {
                    existsPort = true;
                    routerConfig.setPort(Integer.parseInt(value));
                } else if ("maxContentLength".equalsIgnoreCase(name)) {
                    routerConfig.setMaxContentLength(Integer.parseInt(value));
                } else if ("threadNumber".equalsIgnoreCase(name)) {
                    routerConfig.setThreadNumber(Integer.parseInt(value));
                } else if ("dispatchThread".equalsIgnoreCase(name)) {
                    routerConfig.setDispatchThread(Integer.parseInt(value));
                } else if ("ssl".equals(name)) {
                    routerConfig.setSsl(value);
                } else if ("timeout".equals(name)) {
                    routerConfig.setTimeout(Long.parseLong(value));
                }
            }
            Assert.isTrue(!router_property.isEmpty(), "配置错误，每个router必须配置端口, 位置: \n{}", router.asXML());
            // 检查
            Assert.isTrue(routerConfig.getPort() > 0
                            && routerConfig.getPort() <= 65000,
                    "gateway.port 取值范围：1~65000 位置：/config/router/port");
            if (existsPort)
                for (RouterConfig c : gateway.getRouterConfigs()) {
                    Assert.isFalse(c.getPort() == routerConfig.getPort(), "存在重复的路由监听端口: " + c.getPort());
                }
            Assert.isTrue(routerConfig.getMaxContentLength() >= 1024,
                    "gateway.maxContentLength 必须大于等于 1024 位置：/config/router/maxContentLength");
            Assert.isTrue(routerConfig.getThreadNumber() >= 0,
                    "gateway.threadNumber 必须大于等于 0 位置：/config/router/threadNumber");
            Assert.isTrue(routerConfig.getDispatchThread() > 0,
                    "gateway.dispatchThread 必须大于 1 位置：/config/router/dispatchThread");

            // 解析 pass
            List<Element> list_pass = router.elements("pass");
            if (!list_pass.isEmpty()) {
                Assert.isFalse(list_pass.size() > 1, "不能存在多个pass配置, 位置: /config/router/pass");
                Element pass = list_pass.get(0);
                // 解析 http
                List<Element> http = pass.elements("http");
                for (Node node : http) {
                    DefaultElement element = (DefaultElement) node;
                    String source = element.attributeValue("source");
                    Assert.notBlank(source, "router.pass.http.source 不能为空 " + element.asXML());
                    String target = element.attributeValue("target");
                    Assert.notBlank(target, "router.pass.http.target 不能为空 " + element.asXML());
                    String[] split = CommonUtils.removeSpace(target).split(",");
                    List<String> list = new ArrayList<>();
                    for (String str : split) {
                        Assert.isTrue(str.startsWith("http"), "router.pass.http.target 目标地址必须以 http开头" + element.asXML());
                        while (str.endsWith("/"))
                            str = str.substring(0, str.length() - 1);
                        list.add(str);
                    }
                    PassItem item = new PassItem();
                    item.setType(PassType.HTTP);
                    while (source.endsWith("/"))
                        source = source.substring(0, source.length() - 1);
                    item.setSource(source);
                    item.setTarget(list.toArray(new String[0]));
                    routerConfig.getPassHttp().add(item);
                }

                // websocket
                List<Element> websocket = pass.elements("ws");
                for (Node node : websocket) {
                    DefaultElement element = (DefaultElement) node;
                    String source = element.attributeValue("source");
                    Assert.notBlank(source, "router.pass.ws.source 不能为空 " + element.asXML());
                    String target = element.attributeValue("target");
                    Assert.notBlank(target, "router.pass.ws.target 不能为空 " + element.asXML());
                    String[] split = CommonUtils.removeSpace(target).split(",");
                    List<String> list = new ArrayList<>();
                    for (String str : split) {
                        Assert.isTrue(str.startsWith("ws"), "router.pass.ws.target 目标地址必须以 ws 开头" + element.asXML());
                        while (str.endsWith("/"))
                            str = str.substring(0, str.length() - 1);
                        list.add(str);
                    }
                    PassItem item = new PassItem();
                    item.setType(PassType.WS);
                    while (source.endsWith("/"))
                        source = source.substring(0, source.length() - 1);
                    item.setSource(source);
                    item.setTarget(list.toArray(new String[0]));
                    routerConfig.getPassWs().add(item);
                }
            }

            // 解析 accessCheck
            List<Element> accessCheck = router.elements("accesscheck");
            if (!accessCheck.isEmpty()) {
                Assert.isTrue(accessCheck.size() == 1, "不能存在多个accesscheck配置, 位置：/config/router/accesscheck");
                Element access = accessCheck.get(0);
                String enable = access.attributeValue("enable");
                if ("true".equals(enable)) {
                    routerConfig.getAccessCheck().setEnable(true);
                    String rate = access.attributeValue("rate");
                    Assert.notNull(rate, "rate属性不能为空, 位置: /config/router/accesscheck");
                    Assert.isTrue(NumberUtil.isNumber(rate), "rate属性只能为 int 类型, 位置: /config/router/accesscheck");
                    Assert.isTrue(Integer.parseInt(rate) > 0, "rate属性必须大于0, 位置: /config/router/accesscheck");
                    routerConfig.getAccessCheck().setRate(Integer.parseInt(rate));
                }
            }

            // 解析 dist 静态资源
            List<Element> list_static = router.elements("static");
            if (!list_static.isEmpty()) {
                Assert.isFalse(list_static.size() > 1, "不能存在多个static配置, 位置: /config/router/static");
                Element static_ = list_static.get(0);
                List<Element> dist = static_.elements("dist");
                for (Element element : dist) {
                    List<Element> property = element.elements("property");
                    StaticDist sd = new StaticDist();
                    for (Element pro : property) {
                        String name = pro.attributeValue("name");
                        String value = getPropertyValue(pro);
                        if ("enable".equals(name)) {
                            sd.setEnable(Boolean.parseBoolean(value));
                        } else if ("mode".equals(name))
                            sd.setMode(value);
                        else if ("root".equals(name))
                            sd.setRoot(value);
                        else if ("index".equals(name))
                            sd.setIndex(value);
                        else if ("path".equals(name))
                            sd.setPath(value);
                        else if ("fileCache".equals(name))
                            sd.setFileCache(Boolean.parseBoolean(value));
                    }
                    // 检查
                    Assert.isTrue(sd.getMode().equals("vue") || sd.getMode().equals("static"),
                            "router.static.dist.mode 只能是vue或static " + element.asXML());
                    Assert.notBlank(sd.getPath(), "router.static.dist.path 不能为空 " + element.asXML());
                    Assert.isTrue(sd.getPath().startsWith("/"), "gateway.static.dist.path 上下文路径必须 '/' 开头 位置 " + element.asXML());
                    Assert.notBlank(sd.getRoot(), "router.static.dist.root 不能为空 " + element.asXML());
                    Assert.notBlank(sd.getIndex(), "router.static.dist.index 不能为空 " + element.asXML());
                    Assert.isFalse(sd.getIndex().contains("/"), "router.static.dist.index 不能存在 '/' 符号 " + element.asXML());
                    routerConfig.getStaticDist().add(sd);
                }
            }
            gateway.getRouterConfigs().add(routerConfig);
        }

        // 加载 tcp
        List<Element> tcp_ = config.elements("tcp");
        for (Element t : tcp_) {
            DefaultElement tcp = (DefaultElement) t;
            List<Element> elements = tcp.elements("property");
            TcpConfig tcpConfig = new TcpConfig();

            for (Element property : elements) {
                String name = property.attributeValue("name");
                if (StrUtil.isBlank(name))
                    continue;
                name = name.toLowerCase();
                String value = getPropertyValue(property);
                if ("enable".equals(name)) {
                    tcpConfig.setEnable(Boolean.parseBoolean(value));
                } else if ("port".equals(name)) {
                    tcpConfig.setPort(Integer.parseInt(value));
                } else if ("targetHost".equalsIgnoreCase(name)) {
                    tcpConfig.setTargetHost(value);
                } else if ("targetPort".equalsIgnoreCase(name)) {
                    tcpConfig.setTargetPort(Integer.parseInt(value));
                } else if ("threadNumber".equalsIgnoreCase(name)) {
                    tcpConfig.setThreadNumber(Integer.parseInt(value));
                }
            }


            // 解析 accessCheck
            List<Element> accessCheck = tcp.elements("accesscheck");
            if (!accessCheck.isEmpty()) {
                Assert.isTrue(accessCheck.size() == 1, "不能存在多个accesscheck配置, 位置：/config/tcp/accesscheck\n" + t.asXML());
                Element access = accessCheck.get(0);
                String enable = access.attributeValue("enable");
                if ("true".equals(enable)) {
                    tcpConfig.getAccessCheck().setEnable(true);
                    String rate = access.attributeValue("rate");
                    Assert.notNull(rate, "rate属性不能为空, 位置: /config/tcp/accesscheck\n" + t.asXML());
                    Assert.isTrue(NumberUtil.isNumber(rate), "rate属性只能为 int 类型, 位置: /config/tcp/accesscheck\n" + t.asXML());
                    Assert.isTrue(Integer.parseInt(rate) > 0, "rate属性必须大于0, 位置: /config/tcp/accesscheck\n" + t.asXML());
                    tcpConfig.getAccessCheck().setRate(Integer.parseInt(rate));
                }
            }

            // 检查
            Assert.isTrue(tcpConfig.getPort() != null && tcpConfig.getPort() > 0
                            && tcpConfig.getPort() <= 65000,
                    "gateway.tcp.port 取值范围：1~65000 位置：/tcp/port");
            Assert.isTrue(tcpConfig.getTargetPort() != null && tcpConfig.getTargetPort() > 0
                            && tcpConfig.getTargetPort() <= 65000,
                    "gateway.tcp.targetPort 取值范围：1~65000 位置：/tcp/targetPort");
            Assert.isTrue(tcpConfig.getThreadNumber() != null && tcpConfig.getThreadNumber() > 0,
                    "TCP转发最大线程数必须大于 0，位置：/tcp/threadNumber");
            Assert.notBlank(tcpConfig.getTargetHost(),
                    "TCP转发目标主机不能为空，位置：/tcp/targetHost");

            // 检查端口
            for (TcpConfig tt : gateway.getTcpConfigs())
                Assert.isTrue(tt.getPort() != tcpConfig.getPort(), "TCP端口存在重复: " + tt.getPort());
            for (RouterConfig cc : gateway.getRouterConfigs())
                Assert.isTrue(cc.getPort() != tcpConfig.getPort(), "TCP端口存在重复: " + cc.getPort());
            gateway.getTcpConfigs().add(tcpConfig);
        }
        return gateway;
    }

    public static String xmlCheck(String xml) {
        try {
            DocumentHelper.parseText(xml);
        } catch (DocumentException e) {
            return e.getMessage();
        }
        return null;
    }

    private static String getPropertyValue(Element element) {
        String value = element.attributeValue("value");
        return value != null ? value : element.getText();
    }
}
